<template>
  <div class="hy-date-content hy-date-bordWidth">
    <div class="hy-date-bordWidth" v-ClickOutSide="clickOutSide">
      <!-- 文本框，显示输入值 -->
      <input
        class="hy-date-input"
        placeholder="可在此输入文字"
        @focus="getFocus($event)"
        @dblclick="temporaryResetToday"
        :value="modelValue"
        @input="handleInput($event)"
      />
      <!-- 清空按钮 -->
      <i class="hy-date-close-icon" v-show="modelValue !=null && modelValue.length > 0" @click="clearInput">x</i>
      <!-- 警告元素 -->
      <div class="hy-date-messageDiv" v-show="Check.message">{{ Check.message }}</div>
      <Transition name="hy-date-slide-fade">
        <!-- 日期控件 -->
        <div id="hy-date-dateCard" v-show="showBord" class="hy-date-bordWidth hy-date-dateCard">
          <!-- 将元素分割为三个部分，比例为 10:10:80 -->
          <!-- 年度 -->
          <div class="hy-date-yearList hy-date-dateBox">
            <div class="hy-date-numberDiv hy-date-calendarRow" style="border-bottom: 0">年度</div>
            <div id="yearList" class="hy-date-inline" :style="{ height: (listSize + 1) * 24 - 11 + 'px' }">
              <div :id="item" class="hy-date-numberDiv" v-for="item in yearList" :key="item" @click="clickYear(item)"
                   :class="highlight.year == item ? 'hy-date-heightDiv' : ''"> {{ item }}
              </div>
              <div class="hy-date-numberDiv" @click.stop="addYear">更多...</div>
            </div>
            <div class="hy-date-numberDiv" @click.stop="clickYear('0000')"
                 :class="highlight.year == '0000' ? 'hy-date-heightDiv' : ''"> 0000
            </div>
          </div>
          <!-- 月份 -->
          <div class="hy-date-monthList hy-date-dateBox hy-date-padding">
            <div class="hy-date-numberDiv hy-date-calendarRow" style="border-bottom: 0">月份</div>
            <div class="hy-date-noScroll hy-date-inline" :style="{ height: (listSize + 1) * 24 - 11 + 'px' }">
              <div class="hy-date-numberDiv" v-for="item in listSize" :key="item" @click.stop="clickMonth(item)"
                   :class="highlight.month == item ? 'hy-date-heightDiv' : ''"> {{ this.addZero(item.toString()) }}
              </div>
            </div>
            <div class="hy-date-numberDiv" @click.stop="clickMonth('00')"
                 :class="highlight.month == '00' ? 'hy-date-heightDiv' : ''"> 00
            </div>
          </div>
          <!-- 日期 -->
          <div class="hy-date-padding">
            <div class="hy-date-dayCard hy-date-dateBox">
              <div class="hy-date-calendarRow">日期</div>
              <!--  使用二维数组实现 -->
              <div v-for="(item, index) in dayList" :key="index" class="hy-date-dayRow">
                <!-- ${parseInt(day) == 0 ?'border-right:0px':''} 这段样式别改，我也没想明白为啥是这样的…… -->
                <div v-for="(day, dayindex) in item" :key="dayindex"
                     :class="`hy-date-dayDiv ${highlight.day == day ? 'hy-date-heightDiv' : ''}`"
                     :style="`${parseInt(day) % 5 == 0 ? 'border-right:0px' : ''} ${parseInt(day) == 0 ?'border-right:0px':''}`"
                     @click.stop="clickDay(day)">
                  {{ day }}
                </div>
              </div>
              <div class="hy-date-todayDiv">
                <button class="hy-date-button" @click.stop="resetToday">今天</button>
              </div>
            </div>
          </div>
        </div>
      </Transition>
    </div>
  </div>
</template>

<script>
import ClickOutSide from "./ClickOutSide";
import CheckJs from "./CheckRule";

export default {
  props: {
    /**
     * 父组件传递过来的值
     */
    modelValue: {
      type: String
    },
    dbDate: {
      type: String
    }
  },
  /**
   * 显示声明允许向父组件提交的方法
   */
  emits: [
    // 更新显示的值
    "update:modelValue",
    // 更新数据库存储的值
    "update:dbDate"
  ],
  data() {
    return {
      // 年度列表
      yearList: [],
      // 月份列表
      monthList: [],
      // 日期列表，使用二维数组实现
      dayList: [[]],
      // 每组共有几个日期
      dayRowNumber: 5,
      // 当前年度
      FullYear: null,
      // 当前最小年度
      minYear: 0,
      // 当前月份
      Month: null,
      // 当前日期
      Day: null,
      // 需要高亮的日期
      highlight: {
        year: null,
        month: null,
        day: null
      },
      // 年度和月份列表元素的数量
      listSize: 12,
      // 层级
      zIndex: 0,
      // 是否显示日期面板
      showBord: false,
      // 检测文件的引用
      Check: CheckJs
    };
  },

  methods: {
    /**
     * 保存数据到缓存中，保存的数据就是当前组件中产生的数据
     */
    saveCache() {
      // 需要等到子组件想父组件提交数据后，并且重新渲染完成，才可以获取正确的 modelValue
      this.$nextTick(() => {
        localStorage.setItem("hy-date", this.modelValue);
      });
    },
    /**
     * 从缓存中读取数据
     */
    readCache() {
      return localStorage.getItem("hy-date");
    },
    /**
     * 为父组件返回 date对象，由父组件直接调用
     */
    renderDate() {
      let year = this.modelValue.substring(0, 4);
      let month = this.modelValue.substring(4, 6);
      let day = this.modelValue.substring(6, 8);
      let dateString = year + "-" + month + "-" + day;
      let dateObject = new Date(dateString);
      return {
        date: dateObject,
        minusString: dateString,
        slashString: dateString.replaceAll("-", "/")
      };
    },
    /**
     * 用于清空文本框
     */
    clearInput() {
      this.$emit("update:modelValue", "");
      this.Check.resetState();
      this.renderDbDate();
    },
    /**
     * 向父组件提交数据,用于手动输入的情况
     * 需要针对文本框中的值做动态计算
     * @param {Object} e
     */
    handleInput(e) {
      // 手动输入，关闭日期面板
      this.clickOutSide();
      // 获取输入的值
      let dateString = e.target.value;
      // 提交日期数据
      this.$emit("update:modelValue", dateString);
      this.Check.dateString = dateString;
      // 字符串校验
      this.Check.checkValue();

      /**
       * 如果校验当前日期符合既定日期规则，自动格式化
       * 校验有指定标识的
       */
      if (this.Check.standardDate.test(dateString)) {
        this.$nextTick(() => {
          let dateString = this.modelValue.replaceAll("-", "").replaceAll("/", "");
          this.FullYear = dateString.substring(0, 4);
          this.Month = dateString.substring(4, 6);
          this.Day = dateString.substring(6, 8);
        });
      }
      /**
       * 8位日期格式，无需操作，直接保存
       */
      if (this.Check.standard8Length.test(dateString)) {
        this.$nextTick(() => {
          this.FullYear = this.modelValue.substring(0, 4);
          this.Month = this.modelValue.substring(4, 6);
          this.Day = this.modelValue.substring(6, 8);
        });

      }
      /**
       * 19位日期格式，需要单独处理
       */
      if (this.Check.standard19Length.test(dateString)) {
        this.$nextTick(() => {
          let dateArray = [];
          if (dateString.includes("/")) {
            dateArray = (dateString.split(" ")[0]).split("/");
          }
          if (dateString.includes("-")) {
            dateArray = (dateString.split(" ")[0]).split("-");
          }
          let timeString = dateString.split(" ")[1];
          this.FullYear = parseInt(timeString.split(":")[0]) === 0 ? dateArray[0] : "0000";
          this.Month = parseInt(timeString.split(":")[1]) === 0 ? dateArray[1] : "00";
          this.Day = parseInt(timeString.split(":")[2]) === 0 ? dateArray[2] : "00";
        });
      }
      /**
       * 不符合任何规则，清空绑定的对象
       */
      if (!(this.Check.standard19Length.test(dateString) || this.Check.standard8Length.test(dateString) || this.Check.standardDate.test(dateString))) {
        this.FullYear = this.Month = this.Day = null;
      }
      this.getToday();
    },
    /**
     * 文本框获取焦点
     */
    getFocus(e) {
      // 没有值，自动填充数据
      if (!e.target.value) {
        this.getToday();
      }
      this.showBord = true;
      this.$nextTick(() => {
        if (this.readCache() && this.Check.standard8Length.test(this.readCache())) {
          let cacheDate = this.readCache().substr(0, 4);
          if (document.getElementById(cacheDate)) {
            document.getElementById(cacheDate).scrollIntoView();
          }
        }
      });
    },
    /**
     * 当用户点击组件之外的处理函数
     * 模板只关闭日期面板
     */
    clickOutSide() {
      this.showBord = false;
    },
    /**
     * 用户点击年度元素之后的处理函数
     * @param {String} year
     * @param {object} e
     */
    clickYear(year) {

      this.FullYear = year;
      this.initDay();
      this.getToday();
    },
    /**
     * 用户点击月份之后的处理函数
     * @param {number} month
     */
    clickMonth(month) {
      this.Month = this.addZero(month.toString());
      this.initDay();
      this.getToday();
    },
    /**
     * 用户点击日期之后的处理函数
     * @param {number} day
     */
    clickDay(day) {
      this.Day = this.addZero(day.toString());
      this.getToday();
    },
    /**
     * 追加年度,三十年为一批
     */
    addYear() {
      Array.from({ length: 30 }).forEach(() => {
        this.yearList.push(this.minYear--);
      });
    },
    /**
     * 获取今天的日期字符串（格式为档案行业标准的8位）
     * 基本用于初始化和日期面板的点击事件
     */
    getToday(unsave) {
      this.$nextTick(() => {
        if (this.FullYear && this.Month && this.Day) {
          this.$emit("update:modelValue", `${this.FullYear}${this.addZero(this.Month)}${this.addZero(this.Day)}`);
        }
        if (!unsave) {
          this.saveCache();
        }
        this.highlight.year = this.FullYear;
        this.highlight.month = this.Month;
        this.highlight.day = this.Day;
        this.renderDbDate();
      });

    },
    /**
     * 返回数据库存储格式的信息
     * 以时分秒代表年月日
     * 时、分、秒为 01 则代表正常日期，直接显示
     * 时、分、秒为 00 则代表非正常日期，需要转换后显示
     * 年度为 0000 的直接存储 0000
     * 月份与日期为00的存储为 01
     * 正常日期
     * 2023/03/15 01:01:01
     * 年度为 0000 0000/03/15 00:01:01
     * 月份为 00 2023/01/15 01:00:01
     * 日期为 00 2023/03/01 01:01:00
     * 全部为 00 0000/01/01 00:00:00
     */
    renderDbDate() {
      this.$nextTick(() => {
        if (this.modelValue) {
          let hour = parseInt(this.FullYear) === 0 ? "01" : "00";
          let minutes = parseInt(this.Month) === 0 ? "01" : "00";
          let seconds = parseInt(this.Day) === 0 ? "01" : "00";
          this.$emit("update:dbDate", `${this.modelValue} ${hour}:${minutes}:${seconds}`);
        }
      });
    },
    /**
     * 初始化年度，年度值从 data 中获取
     */
    initYear() {
      /**
       * BUG:从缓存中直接读取年度会导致页面最高年度为读取的年度
       */
      let year = new Date().getFullYear();
      /**
       * BUG:指定显示前30年，当用户选择的年度在30年之前则无法正常显示
       */
      let yearSize = 30;
      if (this.readCache() && parseInt(this.readCache().substr(0, 4)) < (year - 30)) {
        yearSize = year - this.readCache().substr(0, 4);
      }
      /**
       * 生成过去的年份
       */
      this.yearList = Array.from({ length: yearSize + 1 + 5 }).map(() => {
        return year--;
      });
      this.minYear = year;
    },
    /**
     * 初始化日期面板，根据年度、月份自动计算日期元素数量
     */
    initDay() {
      // 当月最大日期
      let maxDay = this.getLastDay(this.FullYear, this.Month);
      // 纵向共有几组
      let yGroup = maxDay / this.dayRowNumber;
      // 是否有余数,有余数则需要增加一组
      yGroup = maxDay % this.dayRowNumber ? ++yGroup : yGroup;
      // 定义日期变量
      let dayNumber = 1;
      // 使用临时变量，避免触发VUE更新操作
      let dayListCopy = [[]];
      // 循环打印日期
      for (let y = 0; y < yGroup; y++) {
        dayListCopy[y] = [];
        for (let x = 0; x < this.dayRowNumber; x++) {
          if (dayNumber <= maxDay) {
            dayListCopy[y][x] = this.addZero((dayNumber++).toString());
          }
        }
      }
      /**
       * 最后补充 00 日期
       */
      if (maxDay === 30) {
        dayListCopy[dayListCopy.length + 1] = [];
        dayListCopy[dayListCopy.length - 1][0] = "00";
      } else {
        dayListCopy[dayListCopy.length - 2][dayListCopy[dayListCopy.length - 2].length] = "00";
      }
      this.dayList = dayListCopy;
    },
    /**
     * 获取当月最后一天
     * @param {String} year
     * @param {String} month
     */
    getLastDay(year, month) {
      return new Date(parseInt(year), parseInt(month), 0).getDate();
    },
    /**
     * 为数值增加 0 的前缀，用于规范小月份和日期,例如： 1 => 01
     * @param {string} number
     */
    addZero(number) {
      if (number && number.length === 1) {
        return `0${number}`;
      }
      return number;
    },
    /**
     * 用于鼠标双击事件，不存储数据
     */
    temporaryResetToday() {
      let date = new Date();
      this.FullYear = date.getFullYear();
      this.Month = this.addZero((date.getMonth() + 1).toString());
      this.Day = this.addZero(date.getDate().toString());
      this.getToday(true);
      this.$nextTick(() =>{
        document.getElementById(this.FullYear).scrollIntoView();
      })
    },
    /**
     * 重置日期为今天
     */
    resetToday() {
      let date = new Date();
      this.FullYear = date.getFullYear();
      this.Month = this.addZero((date.getMonth() + 1).toString());
      this.Day = this.addZero(date.getDate().toString());
      this.getToday();
      document.getElementById(new Date().getFullYear().toString()).scrollIntoView();
    }
  },
  /**
   * 初始化组件数据，优先从缓存中获取
   */
  created() {
    let dateCache = this.readCache();
    if (dateCache) {
      this.FullYear = dateCache.substring(0, 4);
      this.Month = dateCache.substring(4, 6);
      this.Day = dateCache.substring(6, 8);
    } else {
      let date = new Date();
      this.FullYear = date.getFullYear();
      this.Month = this.addZero((date.getMonth() + 1).toString());
      this.Day = this.addZero(date.getDate().toString());
    }
  },
  mounted() {
    this.initYear();
    this.$nextTick(() => {
      this.initDay();
    });
  },
  directives: {
    /**
     * 自定义指令，点击组件外部之后触发
     */
    ClickOutSide: ClickOutSide
  }
};
</script>
<style scoped>
.hy-date-close-icon {
  font-style: normal;
  color: #9ea7b4;
  float: right;
  margin-top: -24px;
  position: sticky;
  z-index: 1;
  cursor: pointer;
}

.hy-date-messageDiv {
  color: red;
}

.hy-date-heightDiv {
  background: #2db7f5 !important;
  color: #fff !important;
}

.hy-date-calendarRow {
  text-align: center;
  height: 24px;
  line-height: 24px;
  background: #00a100 !important;
  color: white !important;
}

.hy-date-calendarRow:hover {
  background: green !important;
  color: white !important;
  border: 0 !important;
}

.hy-date-dayRow {
  clear: both;
}

.hy-date-todayDiv {
  text-align: center;
  clear: both;
  margin-top: 100px;
}

.hy-date-button {
  display: inline-block;
  margin-bottom: 0;
  font-weight: 400;
  text-align: center;
  vertical-align: middle;
  -ms-touch-action: manipulation;
  touch-action: manipulation;
  cursor: pointer;
  border: 1px solid #39f;
  white-space: nowrap;
  line-height: 1.5;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  padding: 6px 15px;
  font-size: 12px;
  border-radius: 4px;
  transform: translate3d(0, 0, 0);
  transition: color 0.2s linear, background-color 0.2s linear, border 0.2s linear;
  color: #fff;
  background-color: #39f;
  width: 80%;
}

.hy-date-dayDiv {
  width: 43px;
  height: 24px;
  line-height: 24px;
  float: left;
  border-right: 1px solid #dadada;
  border-bottom: 1px solid #dadada;
  text-align: center;
}

.hy-date-dayDiv:hover {
  background: #e1f0fe;
}

.hy-date-noScroll {
  overflow: hidden !important;
}

.hy-date-dateBox {
  overflow: auto;
  float: left;
}

.hy-date-yearList {
  background: white;
  width: 13%;
}

.hy-date-monthList {
  background: white;
  width: 13%;
  border-right: 1px solid #d4d4d4;
}

.hy-date-dayCard {
  width: 220px;
  background: white;
}

/* 数字显示列表 */
.hy-date-inline {
  width: 100%;
  float: left;
  overflow: auto;
}

/* 数字显示的元素 */
.hy-date-numberDiv {
  border-bottom: 1px solid #eee;
  text-align: center;
  line-height: 24px;
  float: left;
  height: 24px;
  width: 100%;
}

.hy-date-numberDiv:hover {
  border: 1px solid #eee;
  cursor: pointer;
  background: #e1f0fe;
  text-align: center;
}

/* 基础样式 */
.hy-date-input {
  width: 100%;
  line-height: 1.5;
  padding: 4px;
  border: 1px solid #d7dde4;
  /* 圆角 */
  border-radius: 4px;
  background-color: #fff;
  position: relative;
  /* 光标样式 */
  cursor: text;
  /* 动画效果 */
  transition: border 0.2s ease-in-out, background 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}

/* 鼠标悬浮样式 */
.hy-date-input:hover {
  border: 1px solid #2da7e0;
  transition: border 0.2s ease-in-out, background 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}

/* 获取焦点样式 */
.hy-date-input:focus {
  outline: none;
  border-color: #5cadff;
  box-shadow: 0 0 0 2px rgb(51 153 255 / 20%);
}

#hy-date-dateCard {
  position: absolute;
  border-left: 1px solid rgb(225, 225, 225);
  border-bottom: 1px solid rgb(225, 225, 225);
  border-right: 1px solid rgb(225, 225, 225);
}

.hy-date-bordWidth {
  width: 299px;
}

.hy-date-content {
  font-size: 12px;
}

/* 滚动条样式 */
.hy-date-content *::-webkit-scrollbar {
  /*滚动条整体样式*/
  width: 5px; /*高宽分别对应横竖滚动条的尺寸*/
  height: 1px;
}

.hy-date-content *::-webkit-scrollbar-thumb {
  /*滚动条里面深色条*/
  border-radius: 10px;
  box-shadow: inset 0 0 5px rgba(236, 236, 236, 0.1);
  background: #ccc;
}

.hy-date-content *::-webkit-scrollbar-track {
  /*滚动条里面轨道*/
  box-shadow: inset 0 0 5px rgba(236, 236, 236, 0.1);
  border-radius: 10px;
  background: #ededed;
}

/*
  进入和离开动画可以使用不同
  持续时间和速度曲线。
*/
.hy-date-slide-fade-enter-active {
  transition: all 0.3s ease-out;
}

.hy-date-slide-fade-leave-active {
  transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1);
}

.hy-date-slide-fade-enter-from,
.hy-date-slide-fade-leave-to {
  transform: translateX(20px);
  opacity: 0;
}
</style>
